package com.leviassaf.firstandroidapp;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.StreamCorruptedException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import com.facebook.FacebookRequestError;
import com.facebook.HttpMethod;
import com.facebook.Request;
import com.facebook.Response;
import com.facebook.Session;
import com.facebook.SessionState;
import com.facebook.UiLifecycleHelper;
import com.facebook.model.GraphObject;
import com.facebook.model.GraphPlace;
import com.facebook.model.GraphUser;
import com.facebook.model.OpenGraphAction;
import com.facebook.widget.ProfilePictureView;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class SelectionFragment extends Fragment {

	//tag for debugging purposes
	private static final String TAG = "SelectionFragment";
	private ProfilePictureView profilePictureView;
	private TextView textViewUserName;

	//you'll initialize the ListView and set its adapter. There's only one item, the friend picker item, to use in the list that sets up the adapter
	private ListView listView;
	private List<BaseListElement> listBaseListElements;

	//submit button that publishes the Open Graph action through your app
	private Button buttonAnnounce;

	//constant for the Graph API endpoint
	private static final String POST_ACTION_PATH = "me/leviassaf_androidapp:eat";
	//private variable for the progress dialog
	private ProgressDialog progressDialog;

	//private constant that you'll use later on whenever you make a new permissions request. 
	//You'll use it to decide whether to update a session's info in the onActivityResult() method
	private static final int REAUTH_ACTIVITY_CODE = 100;

	//flag that tracks an ongoing reauthorization request
	private boolean booPendingAnnounce;
	//Key used in storing the pendingAnnounce flag
	private static final String PENDING_ANNOUNCE_KEY = "pendingAnnounce";
	//List of additional write permissions being requested
	private static final List<String> PERMISSIONS = Arrays.asList("publish_actions");

	//constant for the Uri where you'll send users if the request error is an authentication-related error
	private static final Uri M_FACEBOOK_URL = Uri.parse("http://m.facebook.com");

	//When the session is open, you'll make an API call to get the user's data and populate the views.
	//The UiLifecycleHelper class can be used in the SelectionFragment to detect session state changes and trigger the user data API call.
	private UiLifecycleHelper uiLifecycleHelper;
	private Session.StatusCallback statusCallback = new Session.StatusCallback() {
		//The Session.StatusCallback listener overrides the call() method to invoke the onSessionStateChange() method
		@Override
		public void call(Session session, SessionState state, Exception exception) {
			onSessionStateChange(session, state, exception);
		}
	};

	public void onActivityResult(int requestCode, int resultCode, android.content.Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		//call the corresponding UiLifecycleHelper method if the REAUTH_ACTIVITY_CODE request code is passed in
		if (requestCode == REAUTH_ACTIVITY_CODE) {
			uiLifecycleHelper.onActivityResult(requestCode, resultCode, data);
		}
		else if (resultCode == Activity.RESULT_OK && requestCode >= 0 && requestCode < listBaseListElements.size()) {
			//call relevant list element's onActivityResult() by delegating the result handling to the appropriate item
			listBaseListElements.get(requestCode).onActivityResult(data);
		}
	}

	@Override
	public void onResume() {
		super.onResume();
		//call the UiLifecycleHelper activity onResume method that is needed to properly keep track of the session
		uiLifecycleHelper.onResume();
	}

	@Override
	public void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);

		//save the list items' state
		for (BaseListElement listElement : listBaseListElements) {
			listElement.onSaveInstanceState(outState);
		}
		//save the pendingAnnounce variable
		outState.putBoolean(PENDING_ANNOUNCE_KEY, booPendingAnnounce);

		//call the UiLifecycleHelper activity onSaveInstanceState method that is needed to properly keep track of the session
		uiLifecycleHelper.onSaveInstanceState(outState);
	}

	@Override
	public void onPause() {
		super.onPause();
		//call the UiLifecycleHelper activity onPause method that is needed to properly keep track of the session
		uiLifecycleHelper.onPause();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		//call the UiLifecycleHelper activity onDestroy method that is needed to properly keep track of the session
		uiLifecycleHelper.onDestroy();
	}

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//initialize the UiLifecycleHelper object and call it's onCreate() method
		uiLifecycleHelper = new UiLifecycleHelper(getActivity(), statusCallback);
		//call the UiLifecycleHelper activity onCreate method that is needed to properly keep track of the session
		uiLifecycleHelper.onCreate(savedInstanceState);
	};

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.selection, container, false);

		//Find the user's profile picture custom view
		profilePictureView = (ProfilePictureView) view.findViewById(R.id.profilePictureView);
		profilePictureView.setCropped(true);
		//Find the username's view
		textViewUserName = (TextView) view.findViewById(R.id.textViewUserName);
		//Find the list view
		listView = (ListView) view.findViewById(R.id.listView);
		//Set up the publish action button
		buttonAnnounce = (Button) view.findViewById(R.id.buttonAnnounce);
		buttonAnnounce.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				handleAnnounce();
			}
		});

		//Initialize the button and list view
		init(savedInstanceState);

		return view;
	}

	//reset the list item view so the user can make a new selection by re-initializing the list view elements. 
	//You'll also want to disable the announce button
	private void init(Bundle savedInstanceState) {
		// initially disable the button and only enable it when a meal is selected
		buttonAnnounce.setEnabled(false);
		
		//Set up the list view items, based on a list of BaseListElement items
		listBaseListElements = new ArrayList<BaseListElement>();
		
		// Add an item for the meal picker
		listBaseListElements.add(new EatListElement(0));
		// Add an item for the place picker
		listBaseListElements.add(new LocationListElement(1));
		// Add an item for the friend picker
		listBaseListElements.add(new PeopleListElement(2));

		//restore the list items' state after they populate
		if (savedInstanceState != null) {
			//Restore the state for each list element
			for (BaseListElement listElement : listBaseListElements) {
				listElement.restoreState(savedInstanceState);
			}
			//restore the pendingAnnounce variable
			booPendingAnnounce = savedInstanceState.getBoolean(PENDING_ANNOUNCE_KEY, false);
		}

		//Set the list view adapter
		listView.setAdapter(new ActionListAdapter(getActivity(), R.id.listView, listBaseListElements));

		Session session = Session.getActiveSession();
		//if a user session is open
		if (session != null && session.isOpened()) {
			//get the user's data
			makeMeRequest(session);
		}
	}

	//method that will respond to session changes and call the makeMeRequest() method if the session's open
	private void onSessionStateChange(final Session session, SessionState sessionState, Exception exception) {
		if (session != null && session.isOpened()) {
			//The SessionState.OPENED_TOKEN_UPDATED state value indicates an updated token which takes place when the publish permissions have been granted
			if (sessionState.equals(SessionState.OPENED_TOKEN_UPDATED)) {
				//Session updated with new permissions so try publishing once more
				tokenUpdated();
			} else {
				//Get the user's data.
				makeMeRequest(session);
			}
		}
	}

	//method that requests the user's data
	private void makeMeRequest(final Session session) {
		//Request is used to construct a single request to make Facebook API calls
		//A request requiring an authenticated user must pass in an open Session
		//newMeRequest is a static method of the Request class that returns user data
		//When setting up the request, we define a callback to handle the response that is an object of type GraphUser (GraphUserCallback). 
		//We'll process the returned user object and display the user's profile picture with the ProfilePictureView object to display the user's name with a TextView
		Request request = Request.newMeRequest(
			session, new Request.GraphUserCallback() {
				@Override
				public void onCompleted(GraphUser graphUser, Response response) {
					//If the response is successful, the Facebook SDK will extend the access token in the background if needed
					if (session == Session.getActiveSession()) {
						if (graphUser != null) {
							//Set the id for the ProfilePictureView view that in turn displays the profile picture.
							profilePictureView.setProfileId(graphUser.getId());
							//GraphUser is an interface that provides a strongly-typed representation of a Facebook user
							textViewUserName.setText(graphUser.getName());
						}
					}
					if (response.getError() != null) {
						//Handle errors found when fetching user data
						handleError(response.getError());
					}
				}
			}
		);
		request.executeAsync();
	}

	//private inner class that extends ArrayAdapter so you can customize the list item view.
	//The ArrayAdapter class is a concrete class of BaseAdapter that implements the ListAdapter interface. 
	//The ArrayAdapter is the class you'll use to set up the ListView adapter as it can take in an array of arbitrary objects and use them to set up the display. 
	//You'll want to use arbitrary objects because you'll set up a custom object to represent the meal, friend picker and place picker items.
	private class ActionListAdapter extends ArrayAdapter<BaseListElement> {
		private List<BaseListElement> listBaseListElements;

		//the constructor for the adapter class takes in a List<BaseListElement> list of elements and registers itself to receive data change notifications for each item
		public ActionListAdapter(Context context, int resourceId, List<BaseListElement> listElements) {
			super(context, resourceId, listElements);
			this.listBaseListElements = listElements;
			//Set up as an observer for list item changes to refresh the view
			for (int i = 0; i < listElements.size(); i++) {
				listElements.get(i).setAdapter(this);
			}
		}

		//The getView() method uses the R.layout.listitem layout to set up the view. 
		//This method also calls the specific list item to set up its click event listener, the icon and text info.
		//The item view for this sample app consists of an customized ImageView consisting of an icon for the action, 
		//a description text for the action and placeholder text that will be replaced with any user-selected info 
		@Override
		public View getView(int position, View convertView, ViewGroup viewGroup) {
			View view = convertView;
			if (view == null) {
				LayoutInflater layoutInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
				view = layoutInflater.inflate(R.layout.listitem, null);
			}
			BaseListElement baseListElement = listBaseListElements.get(position);
			if (baseListElement != null) {
				view.setOnClickListener(baseListElement.getOnClickListener());
				ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
				TextView textView1 = (TextView) view.findViewById(R.id.textView1);
				TextView textView2 = (TextView) view.findViewById(R.id.textView2);
				if (imageView != null) {
					imageView.setImageDrawable(baseListElement.getIcon());
				}
				if (textView1 != null) {
					textView1.setText(baseListElement.getText1());
				}
				if (textView2 != null) {
					textView2.setText(baseListElement.getText2());
				}
			}
			return view;
		}
	}

	//inner private class called PeopleListElement that represents the friend picker list item
	private class PeopleListElement extends BaseListElement {
		private List<GraphUser> listGraphUserSelectedUsers;
		//key for the bundle 
		private static final String FRIENDS_KEY = "friends";

		public PeopleListElement(int requestCode) {
			super(getActivity().getResources().getDrawable(
					R.drawable.action_people), getActivity().getResources()
					.getString(R.string.action_people), getActivity()
					.getResources().getString(R.string.action_people_default),
					requestCode);
		}

		//The selected friends is a list of type GraphUser. 
		//To save the info, convert the list to a byte array
		private byte[] getByteArray(List<GraphUser> users) {
			//convert the list of GraphUsers to a list of String where each element is the JSON representation of the GraphUser so it can be stored in a Bundle
			List<String> usersAsString = new ArrayList<String>(users.size());
			for (GraphUser graphUser : users) {
				usersAsString.add(graphUser.getInnerJSONObject().toString());
			}
			;
			try {
				ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
				new ObjectOutputStream(byteArrayOutputStream)
						.writeObject(usersAsString);
				return byteArrayOutputStream.toByteArray();
			} catch (IOException e) {
				Log.e(TAG, "Unable to serialize users.", e);
			}
			return null;
		}

		//To save the selected friends, you'll override the onSaveInstanceState() method in the PeopleListElement class. 
		//The info is saved in the bundle as a byte array
		@Override
		protected void onSaveInstanceState(Bundle bundle) {
			if (listGraphUserSelectedUsers != null) {
				bundle.putByteArray(FRIENDS_KEY, getByteArray(listGraphUserSelectedUsers));
			}
		}

		//helper method that converts a byte array to a List
		private List<GraphUser> restoreByteArray(byte[] bytes) {
			try {
				@SuppressWarnings("unchecked")
				List<String> usersAsString = (List<String>) (new ObjectInputStream(
						new ByteArrayInputStream(bytes))).readObject();
				if (usersAsString != null) {
					List<GraphUser> users = new ArrayList<GraphUser>(
							usersAsString.size());
					for (String strUser : usersAsString) {
						GraphUser graphUser;
						graphUser = GraphObject.Factory.create(new JSONObject(
								strUser), GraphUser.class);
						users.add(graphUser);
					}
					return users;
				}
			} catch (OptionalDataException e) {
				e.printStackTrace();
			} catch (StreamCorruptedException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (JSONException e) {
				e.printStackTrace();
			}
			return null;
		}

		//To restore any saved selected friends, you'll override the restoreState() method in the PeopleListElement class. 
		//The saved state is passed in as a bundle and you'll read back a byte array representation of the saved selection 
		//and use a helper method to convert this to a GraphUser list. 
		//You can then call the previously defined setUsersText() method to display the selected friends that were stored
		@Override
		protected boolean restoreState(Bundle savedState) {
			byte[] bytes = savedState.getByteArray(FRIENDS_KEY);
			if (bytes != null) {
				listGraphUserSelectedUsers = restoreByteArray(bytes);
				setUsersText();
				return true;
			}
			return false;
		}

		@Override
		protected View.OnClickListener getOnClickListener() {
			return new View.OnClickListener() {

				@Override
				public void onClick(View v) {
					//start the picker
					startPickerActivity(PickerActivity.FRIEND_PICKER, getRequestCode());
				}
			};
		}

		//helper method that will set the selected friends text. The helper method controls how the friends display. 
		//If one or two friends are selected, those friends' names will display. 
		//If more than two friends are selected, the first friend's name will display with a count value of the other selected friends
		private void setUsersText() {
			String strText = null;
			if (listGraphUserSelectedUsers != null) {
				if (listGraphUserSelectedUsers.size() == 1) {
					//If there is one friend
					strText = String.format(
						getResources().getString(R.string.single_user_selected),
						listGraphUserSelectedUsers.get(0).getName()
					);
				} else if (listGraphUserSelectedUsers.size() == 2) {
					//If there are two friends
					strText = String.format(
						getResources().getString(R.string.two_users_selected), 
						listGraphUserSelectedUsers.get(0).getName(), 
						listGraphUserSelectedUsers.get(1).getName()
					);
				} else if (listGraphUserSelectedUsers.size() > 2) {
					//If there are more than two friends
					strText = String.format(
						getResources().getString(R.string.multiple_users_selected),
						listGraphUserSelectedUsers.get(0).getName(),
						(listGraphUserSelectedUsers.size() - 1)
					);
				}
			}
			if (strText == null) {
				//If no text, use the placeholder text
				strText = getResources().getString(R.string.action_people_default);
			}
			//Set the text in list element. This will notify the adapter that the data has changed to refresh the list view
			setText2(strText);
		}

		//override the onActivityResult() method that is called when the friend picker returns. 
		//In the method definition, you'll get the selected users from the application, 
		//set the results TextView text and finally notify the ListView's adapter that the data has changed
		@Override
		protected void onActivityResult(Intent data) {
			super.onActivityResult(data);
			listGraphUserSelectedUsers = ((ScrumptiousApplication) getActivity()
					.getApplication()).getSelectedUsers();
			setUsersText();
			notifyDataChanged();
		}

		@Override
		protected void populateOGAction(OpenGraphAction action) {
			if (listGraphUserSelectedUsers != null) {
				//sets the list of profiles that were tagged in the action
				action.setTags(listGraphUserSelectedUsers);
			}
		}

	}

	//inner private class called LocationListElement that represents the place picker list item
	private class LocationListElement extends BaseListElement {
		private GraphPlace graphPlaceSelected = null;
		private static final String PLACE_KEY = "place";

		//helper method that will set the selected place text
		private void setPlaceText() {
			String strText = null;
			if (graphPlaceSelected != null) {
				strText = graphPlaceSelected.getName();
			}
			if (strText == null) {
				strText = getResources().getString(
						R.string.action_location_default);
			}
			setText2(strText);
		}

		//show the selected place after getting this info from the application where it was stored by the place picker
		//override the onActivityResult() method that is called when the place picker returns. 
		//In the method definition, you'll get the selected place from the application, 
		//set the results TextView text and finally notify the ListView that the data has changed
		@Override
		protected void onActivityResult(Intent data) {
			graphPlaceSelected = ((ScrumptiousApplication) getActivity().getApplication()).getSelectedPlace();
			setPlaceText();
			notifyDataChanged();
		}

		//restore the selected place to ensure that the displayed place and restored with the SelectionFragment
		@Override
		protected boolean restoreState(Bundle savedState) {
			String strPlace = savedState.getString(PLACE_KEY);
			if (strPlace != null) {
				try {
					//Read back a string representation of the saved selection and use the GraphObject.Factory class to convert this to a GraphPlace object
					graphPlaceSelected = GraphObject.Factory.create(new JSONObject(strPlace), GraphPlace.class);
					//call the previously defined setPlaceText() method to display the restored selected place
					setPlaceText();
					return true;
				} catch (JSONException e) {
					Log.e(TAG, "Unable to deserialize place.", e);
				}
			}
			return false;
		}

		//save the selected place to ensure that the displayed place is saved with the SelectionFragment
		@Override
		protected void onSaveInstanceState(Bundle bundle) {
			if (graphPlaceSelected != null) {
				bundle.putString(PLACE_KEY, graphPlaceSelected.getInnerJSONObject().toString());
			}
		}

		public LocationListElement(int requestCode) {
			super(getActivity().getResources().getDrawable(
					R.drawable.action_location), getActivity().getResources()
					.getString(R.string.action_location),
					getActivity().getResources().getString(
							R.string.action_location_default), requestCode);
		}

		@Override
		protected OnClickListener getOnClickListener() {
			return new View.OnClickListener() {

				@Override
				public void onClick(View view) {
					//launch the picker when place picker item is clicked and pass in the place picker Uri
					startPickerActivity(PickerActivity.PLACE_PICKER, getRequestCode());
				}
			};
		}

		@Override
		protected void populateOGAction(OpenGraphAction action) {
			if (graphPlaceSelected != null) {
				action.setPlace(graphPlaceSelected);
			}
		}
	}

	//inner private class called EatListElement that represents the meal picker list item
	private class EatListElement extends BaseListElement {
		//variables for the selected meal and the meal object URL
		private final String[] arrStrFoodChoices;
		private final String[] arrStrFoodUrls;

		private String strFoodChoiceUrl = null;
		private String strFoodChoice = null;

		//keys you'll use in the bundle for the meal info
		private static final String FOOD_KEY = "food";
		private static final String FOOD_URL_KEY = "food_url";

		public EatListElement(int requestCode) {
			super(getActivity().getResources().getDrawable(
					R.drawable.action_eating), getActivity().getResources()
					.getString(R.string.action_eating), getActivity()
					.getResources().getString(R.string.action_eating_default),
					requestCode);
			arrStrFoodChoices = getActivity().getResources().getStringArray(
					R.array.food_types);
			arrStrFoodUrls = getActivity().getResources().getStringArray(
					R.array.food_og_urls);
		}

		//helper method to set the selected meal text
		//enable or disable the button based on if a meal is selected
		private void setFoodText() {
			if (strFoodChoice != null && strFoodChoiceUrl != null) {
				setText2(strFoodChoice);
				buttonAnnounce.setEnabled(true);
			} else {
				setText2(getActivity().getResources().getString(
						R.string.action_eating_default));
				buttonAnnounce.setEnabled(false);
			}
		}

		@Override
		protected OnClickListener getOnClickListener() {
			return new View.OnClickListener() {

				@Override
				public void onClick(View v) {
					showMealOptions();
				}
			};
		}

		//helper method in the EatListElement class that displays the meal picker
		private void showMealOptions() {
			String strTitle = getActivity().getResources().getString(R.string.select_meal);
			//For the AlertDialog, set the item choices and define an item click listener
			AlertDialog.Builder builder = new Builder(getActivity());
			builder.setTitle(strTitle);
			builder.setCancelable(true);
			builder.setItems(
				arrStrFoodChoices,
				new DialogInterface.OnClickListener() {
					//first save the meal selection based on the index of the item clicked, 
					//then set the text and finally notify the adapter of the data changes
					@Override
					public void onClick(DialogInterface dialog, int which) {
						strFoodChoice = arrStrFoodChoices[which];
						strFoodChoiceUrl = arrStrFoodUrls[which];
						setFoodText();
						notifyDataChanged();
					}
				}
			);
			builder.show();
		}

		//save the selected meal info.
		//The info is saved in the bundle as a string
		@Override
		protected void onSaveInstanceState(Bundle bundle) {
			if (strFoodChoice != null && strFoodChoiceUrl != null) {
				bundle.putString(FOOD_KEY, strFoodChoice);
				bundle.putString(FOOD_URL_KEY, strFoodChoiceUrl);
			}
		}

		//restore a saved meal selection info
		@Override
		protected boolean restoreState(Bundle savedState) {
			//Read back the data that are stored as strings, 
			//then call the setFoodText() method to display the restored selected meal
			String strFood = savedState.getString(FOOD_KEY);
			String strFoodUrl = savedState.getString(FOOD_URL_KEY);
			if (strFood != null && strFoodUrl != null) {
				strFoodChoice = strFood;
				strFoodChoiceUrl = strFoodUrl;
				setFoodText();
				return true;
			}
			return false;
		}

		@Override
		protected void populateOGAction(OpenGraphAction action) {
			if (strFoodChoiceUrl != null) {
				EatAction eatAction = action.cast(EatAction.class);
				MealGraphObject meal = GraphObject.Factory.create(MealGraphObject.class);
				meal.setUrl(strFoodChoiceUrl);
				eatAction.setMeal(meal);
			}
		}
	}

	// method that starts a new intent for the PickerActivity class with data that corresponds to the Uri the PickerActivity is expecting
	//the method also takes a request code so you can properly process the activity's result when it returns
	private void startPickerActivity(Uri uriData, int requestCode) {
		Intent intent = new Intent();
		intent.setData(uriData);
		intent.setClass(getActivity(), PickerActivity.class);
		startActivityForResult(intent, requestCode);
	}

	//The GraphObject interface is the basic interface used by the Facebook SDK to represent objects in the social graph. 
	//It provides typed access to these objects. 
	//You can set up a meal object representation by extending the GraphObject interface
	//The GraphObject represents the eat action parameters, like the selected meal, friends and place
	private interface MealGraphObject extends GraphObject {
		//A URL
		public String getUrl();
		public void setUrl(String url);

		//An ID
		public String getId();
		public void setIId(String id);
	}

	//The OpenGraphAction interface that implements GraphObject, 
	//provides a strongly typed representation of an Open Graph action and allows you to set and get Open Graph action properties such the place or tags. 
	//You can set up an eat action representation by extending the OpenGraphAction interface and adding a meal object
	private interface EatAction extends OpenGraphAction {
		//The meal object
		public MealGraphObject getMeal();

		public void setMeal(MealGraphObject meal);
	}

	//method that will initiate a request for new permissions. 
	//This method creates a new Session.NewPermissionsRequest and sets the request code that is returned to onActivityResult()
	private void requestPublishPermissions(Session session) {
		if (session != null) {
			//Session.NewPermissionsRequest is used to ask for publish permissions before posting the story
			Session.NewPermissionsRequest newPermissionsRequest = new Session.NewPermissionsRequest(this, PERMISSIONS);
			newPermissionsRequest.setRequestCode(REAUTH_ACTIVITY_CODE);
			session.requestNewPublishPermissions(newPermissionsRequest);

		}
	}

	//method that initiates the publishing of an action. 
	//Before it actually publishes an action, it checks that the required permissions are available. 
	//If the required permissions are missing, it calls the requestPublishPermissions() method to request the required permissions
	private void handleAnnounce() {
		booPendingAnnounce = false;
		Session session = Session.getActiveSession();
		if (session == null || !session.isOpened()) {
			return;
		}
		List<String> permissions = session.getPermissions();
		if (!permissions.containsAll(PERMISSIONS)) {
			booPendingAnnounce = true;
			requestPublishPermissions(session);
			return;
		}
		//Show a progress dialog because sometimes the requests can take a while. This dialog contains a text message
		progressDialog = ProgressDialog.show(getActivity(), "", getActivity().getResources().getString(R.string.progress_dialog_text), true);
		//Run this in a background thread since we don't want to block the main thread. Create a new AsyncTask that returns a Response object.
		//Response encapsulates the response from the Facebook API call
		AsyncTask<Void, Void, Response> asyncTask = new AsyncTask<Void, Void, Response>(){
			@Override
			protected Response doInBackground(Void... params) {
				//Create an eat action
				EatAction eatAction = GraphObject.Factory.create(EatAction.class);
				//Populate the action with the POST parameters: the meal, friends, and place info
				for (BaseListElement baseListElement: listBaseListElements){
					baseListElement.populateOGAction(eatAction);
				}
				//Set up a request with the active session, set up an HTTP POST to the eat action endpoint
				Request request = new Request(Session.getActiveSession(), POST_ACTION_PATH, null, HttpMethod.POST);
				//Add the post parameter, the eat action
				request.setGraphObject(eatAction);
				//Execute the request synchronously in the background and return the response
				return request.executeAndWait();
			}

			@Override
			protected void onPostExecute(Response result) {
				//When the task completes, process the response on the main thread
				onPostActionResponse(result);;
			}
			
		};
		//Execute the task
		asyncTask.execute();
	}

	//method that can be called by the when the additional permissions request completes successfully
	private void tokenUpdated() {
		//if a publish action is in progress awaiting a successful reauthorization
		if (booPendingAnnounce) {
			//Publish the action
			handleAnnounce();
		}
	}

	//method that you'll call if there are any errors when the action is published. 
	//Based on the error classification, you'll show a relevant message and take appropriate action
	private void handleError(FacebookRequestError error) {
		DialogInterface.OnClickListener onClickListener = null;
		String strDialogBody = null;
		if (error == null) {
			//There was no response from the server
			strDialogBody = getString(R.string.error_dialog_default_text);
		} else {
			switch (error.getCategory()) {
			case AUTHENTICATION_RETRY:
				//Tell the user what happened by getting the message id, and retry the operation later
				String strUserAction = (error.shouldNotifyUser()) ? ""
						: getString(error.getUserActionMessageId());
				strDialogBody = getString(R.string.error_authentication_retry,
						strUserAction);
				onClickListener = new DialogInterface.OnClickListener() {

					@Override
					public void onClick(DialogInterface dialog, int which) {
						//Take the user to the mobile site
						Intent intent = new Intent(Intent.ACTION_VIEW, M_FACEBOOK_URL);
						startActivity(intent);
					}
				};
				break;
			case AUTHENTICATION_REOPEN_SESSION:
				//Close the session and reopen it
				strDialogBody = getString(R.string.error_authentication_reopen);
				onClickListener = new DialogInterface.OnClickListener() {

					@Override
					public void onClick(DialogInterface dialog, int which) {
						Session session = Session.getActiveSession();
						if (session != null && !session.isClosed()) {
							session.closeAndClearTokenInformation();
						}
					}
				};
				break;
			case PERMISSION:
				//A permissions-related error
				strDialogBody = getString(R.string.error_permission);
				onClickListener = new DialogInterface.OnClickListener() {

					@Override
					public void onClick(DialogInterface dialog, int which) {
						booPendingAnnounce = true;
						//Request publish permission
						requestPublishPermissions(Session.getActiveSession());
					}
				};
				break;
			case SERVER:
			case THROTTLING:
				//This is usually temporary, don't clear the fields, and ask the user to try again
				strDialogBody = getString(R.string.error_server);
				break;
			case BAD_REQUEST:
				//This is likely a coding error, ask the user to file a bug
				strDialogBody = getString(R.string.error_bad_request, error.getErrorMessage());
				break;
			case OTHER:
			case CLIENT:
			default:
				//An unknown issue occurred, this could be a code error, or a server side issue, log the issue, and either ask the user to retry, or file a bug
				strDialogBody = getString(R.string.error_unknown,
						error.getErrorMessage());
				break;
			}
		}
		//Show the error and pass in the listener so action can be taken, if necessary.
		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
		builder.setPositiveButton(R.string.error_dialog_button_text, onClickListener);
		builder.setTitle(R.string.error_dialog_title);
		builder.setMessage(strDialogBody).show();
	}

	//private inner interface that extends GraphObject and allows typed access to an ID
	private interface PostResponse extends GraphObject {
		String getId();
	}

	//method that you'll call from the background publishing task when it's complete. 
	//If there's a response and a response ID available, format and display the result in a dialog. 
	//Otherwise call the handleError() method to take care of handling errors
	private void onPostActionResponse(Response response) {
		if (progressDialog != null){
			progressDialog.dismiss();
			progressDialog = null;
		}
		if (getActivity() == null) {
			//if the user removes the app from the website, then a request will have caused the session to close (since the token is no longer valid),
	        // which means the splash fragment will be shown rather than this one, causing activity to be null. 
	        // If the activity is null, then we cannot show any dialogs, so we return.
			return;
		}
		PostResponse postResponse = response.getGraphObjectAs(PostResponse.class);
		//reset the view when an action is published successfully
		if (postResponse != null && postResponse.getId() != null) {
			String strDialogBody = String.format(getString(R.string.result_dialog_text), postResponse.getId());
			AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
			builder.setPositiveButton(R.string.result_dialog_button_text, null);
			builder.setTitle(R.string.result_dialog_title);
			builder.setMessage(strDialogBody).show();
			//Reset the button and list view
			init(null);
		} else {
			handleError(response.getError());
		}
	}

	
}
